{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8cb4192a",
   "metadata": {},
   "source": [
    "# 函数\n",
    "\n",
    "- 学习：函数的创建和调用以及参数的传递\n",
    "\n",
    "- 函数是能够**反复调用的实现特定功能的计算机代码，是一种特定的代码组结构** （类似于能够实现标准化、规模化生产）\n",
    "\n",
    "    - 代码块+封装\n",
    "\n",
    "- 函数的作用：\n",
    "    1. 提升代码的重复利用率，避免重复开发相同代码\n",
    "    2. 提高程序开发效率（分工）\n",
    "    3. 便于程序维护"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "631bf2c6",
   "metadata": {},
   "source": [
    "## 定义函数\n",
    "\n",
    "- 以 `def` 关键词开头，后面接函数名称和圆括号(),由:结束\n",
    "\n",
    "- 传入的参数写在圆括号内\n",
    "\n",
    "- 以 return 结束函数，后面加需要返回的函数结果；如果没有需要返回的结果，可以不写return\n",
    "\n",
    "- 调用函数： 函数名()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "380c45b9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "very good\n",
      "nice\n",
      "hey\n"
     ]
    }
   ],
   "source": [
    "# 基本函数格式\n",
    "# 函数名称不使用中文，不能以数字开头，可以使用_\n",
    "def fun1():\n",
    "    print('very good')\n",
    "    print('nice')\n",
    "    print('hey')\n",
    "    return     \n",
    "\n",
    "# 调用函数\n",
    "fun1()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "864dbe83",
   "metadata": {},
   "source": [
    "```{note}\n",
    "- 调用函数时，函数会从头开始执行，当函数中的代码执行完毕后，则函数调用结束。\n",
    "- 函数中如果存在return语句，执行到return语句时，函数调用结束\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c2d84a6",
   "metadata": {},
   "source": [
    "## 函数文档\n",
    "\n",
    "- 在函数内，用三个单引号或三个双引号加说明\n",
    "- 使用`help(函数名)`调用函数的相关说明\n",
    "- 使用`函数名.__doc__`直接查看文档说明"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e828820f",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "def mysum(num):\n",
    "    '''\n",
    "    这是一个计算加总的函数\n",
    "    参数是int类型\n",
    "    返回值是int类型\n",
    "    '''\n",
    "    pass\n",
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d6536615",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function mysum in module __main__:\n",
      "\n",
      "mysum(num)\n",
      "    这是一个计算加总的函数\n",
      "    参数是int类型\n",
      "    返回值是int类型\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(mysum)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "07f91ee5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\\n    这是一个计算加总的函数\\n    参数是int类型\\n    返回值是int类型\\n    '"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mysum.__doc__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e9543cd5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function max in module builtins:\n",
      "\n",
      "max(...)\n",
      "    max(iterable, *[, default=obj, key=func]) -> value\n",
      "    max(arg1, arg2, *args, *[, key=func]) -> value\n",
      "    \n",
      "    With a single iterable argument, return its biggest item. The\n",
      "    default keyword-only argument specifies an object to return if\n",
      "    the provided iterable is empty.\n",
      "    With two or more arguments, return the largest argument.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(max)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b57a739",
   "metadata": {},
   "source": [
    "## 形参 & 实参\n",
    "\n",
    "- 形参： 形式上的参数，声明/定义函数时()中的参数\n",
    "- 实参： 实际上的参数，调用函数时()中的参数\n",
    "\n",
    "```{note}\n",
    "- 注意： 实参将值传递给形参的过程本质上就是简单的变量赋值\n",
    "- 函数调用时，参数必须以正确的顺序传入函数，实参数量必须和声明时的一样\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43c39200",
   "metadata": {},
   "source": [
    "## 函数的参数 （形参）\n",
    "\n",
    "- 位置参数\n",
    "- 默认参数\n",
    "- 可变参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f5c68e5",
   "metadata": {},
   "source": [
    "### 位置参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "db9ccf73",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算数值的平放\n",
    "def power(x):\n",
    "    return x**2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86819ce7",
   "metadata": {},
   "source": [
    "对于`power(x)`函数，参与x是**位置参数**，也叫做**必选参数** —— 调用函数时，必须要传入的参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "95c76eeb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9\n",
      "64\n"
     ]
    }
   ],
   "source": [
    "print(power(3))\n",
    "print(power(8))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1fa423f4",
   "metadata": {},
   "source": [
    "Q: 如果我们想计算三次方，该怎么做？\n",
    "```python\n",
    "def power3(x):\n",
    "    return x**3\n",
    "```\n",
    "Q: 如果四次方呢？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "46d6f69b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def power(x,n):\n",
    "    return x**n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b2ccd22",
   "metadata": {},
   "source": [
    "x和n是power(x,n)函数的位置参数；调用函数时，传入的两个值会分为赋给x和n。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b1cfb996",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "32\n",
      "25\n"
     ]
    }
   ],
   "source": [
    "print(power(2,5))\n",
    "print(power(5,2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "0ad36450",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "power() missing 1 required positional argument: 'n'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_14172/2828352081.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mpower\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: power() missing 1 required positional argument: 'n'"
     ]
    }
   ],
   "source": [
    "power(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f481eb31",
   "metadata": {},
   "source": [
    "### 默认参数\n",
    "\n",
    "- 定义的参数带有默认值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "1bc67985",
   "metadata": {},
   "outputs": [],
   "source": [
    "def power(x, n = 3):\n",
    "    return x**n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1328f7ac",
   "metadata": {},
   "source": [
    "- `power(x, n = 3)`中，`n`是默认函数，如果调用函数时，不传入n的值，会默认令`n=3`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "d6e65046",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "27"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "040fe8e7",
   "metadata": {},
   "source": [
    "- 默认参数可以简化函数的调用。设置默认参数时，有几点要注意：\n",
    "    - 定义函数时，位置参数在前，默认参数在后，否则Python的解释器会报错；\n",
    "  \n",
    "    - 当函数有多个参数时，把变化大的参数放前面，变化小的参数放后面。变化小的参数就可以作为默认参数。\n",
    "\n",
    "    - 有多个默认参数时，调用的时候，既可以按顺序提供默认参数，也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时，需要把参数名写上。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "80f4e413",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-03-10T04:11:06.732034Z",
     "start_time": "2022-03-10T04:11:06.706927Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "姓名：May,年龄:20,性别：male, 专业：econ\n",
      "姓名：May,年龄:20,性别：male, 专业：econ\n"
     ]
    }
   ],
   "source": [
    "# 带有默认值的参数\n",
    "def fun1(name,age=18,sex='female',major = 'econ'):\n",
    "    print('姓名：{},年龄:{},性别：{}, 专业：{}'.format(name,age,sex,major))\n",
    "    return\n",
    "\n",
    "fun1('May', 20, 'male')\n",
    "fun1('May', age=20, sex='male')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "d5a813e2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2, 3, 4, 'end']\n",
      "[4, 5, 6, 'end']\n"
     ]
    }
   ],
   "source": [
    "# 例子\n",
    "def add_end(x = []):\n",
    "    x.append('end')\n",
    "    return x\n",
    "\n",
    "print(add_end([2,3,4]))\n",
    "print(add_end([4,5,6]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "06705074",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['end', 'end']\n",
      "['end', 'end', 'end']\n"
     ]
    }
   ],
   "source": [
    "print(add_end())\n",
    "print(add_end())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "136216ea",
   "metadata": {},
   "source": [
    "```{note}\n",
    "Pytho函数在定义的时候，默认参数x的值就被计算出来了，即`[]`，因为默认参数x也是一个变量，它指向对象`[]`，所以每次调用该函数，如果改变了x的内容，则下次调用时，默认参数的内容就变了，不再是函数定义时的`[]`了。\n",
    "\n",
    "- 默认参数必须指向不变对象！！\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "40a69fb4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 修改\n",
    "def add_end(x = None):\n",
    "    if x is None:\n",
    "        x = [];\n",
    "\n",
    "    x.append('end')\n",
    "    return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "f19ea46c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['end']"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_end()\n",
    "add_end()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e02f466",
   "metadata": {},
   "source": [
    "### 可变参数\n",
    "\n",
    "- 参数个数不确定时，可以用参数名前加`*` 或 `**`的方式告诉python会传入不确定数量的参数 （动态空间）\n",
    "  - `*arg`, `**karg`\n",
    "  - `*`：表示直接传入参数，在函数内部，arg接收的是一个tuple类型 \n",
    "  - `**`: 表示传入带有**默认值（或关键字）的参数**，在函数内部，karg接收的是一个dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "4a49422b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 2, 3, 'ji')\n"
     ]
    }
   ],
   "source": [
    "def fun2(*arg):\n",
    "    print(arg)\n",
    "    \n",
    "# 以元组(tuple)的方式传入参数\n",
    "fun2(1,2,3,'ji')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c67f0cee",
   "metadata": {},
   "source": [
    "- 练习：计算数字之和, a+b+c+d+..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "14746d16",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def cal_sum(a_li):\n",
    "    _ = 0\n",
    "    for i in a_li:\n",
    "        _ += i\n",
    "    return _\n",
    "\n",
    "cal_sum([1,2,3,4]) # 在调用时，需要先把数字组合成一个list 或 tuple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "a2390184",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用可变参数\n",
    "def cal_sum2(*a_li):\n",
    "    total = 0\n",
    "    for i in a_li:\n",
    "        total += i\n",
    "    return total\n",
    "\n",
    "cal_sum2(1,2,3,4,5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41df74a7",
   "metadata": {},
   "source": [
    "Q: 如果想在cal_sum2()函数中传入 list 应该怎么做？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "60d60a2d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ls = [1,2,3]\n",
    "cal_sum2(ls[0],ls[1],ls[2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "49c2b9ed",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cal_sum2(*ls)  # 在ls前加上星号，可以把list or tuple 作为可变参数传入函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "15b0c85f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'a': 1, 'b': 2, 'c': 3}\n"
     ]
    }
   ],
   "source": [
    "# **kargs表示收集关键字参数（带有默认值的参数）\n",
    "def fun1(**kargs):\n",
    "    print(kargs)\n",
    "\n",
    "fun1(a=1,b=2,c=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ce43270",
   "metadata": {},
   "source": [
    "- 定义函数时，需将*arg放在**karg之前，否则会有“Invalid Syntax”的语法错误。\n",
    "- arg与kargs均可以换成其他名字"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37a537c3",
   "metadata": {},
   "source": [
    "### 参数组合\n",
    "\n",
    "- 在Pyhon的函数定义中，位置参数、默认参数和可变参数可以组合使用。\n",
    "- 注意，参数定义的顺序必须是：**位置参数、默认参数、可变参数**。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "a62f8bea",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "(2, 3, 'ty')\n",
      "{'c': 2, 'n': 10}\n"
     ]
    }
   ],
   "source": [
    "def test(value1,*arg, **kargs):\n",
    "    print(value1)\n",
    "    print(arg)\n",
    "    print(kargs)\n",
    "    \n",
    "test(1,2,3,'ty',c=2,n=10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "b58a8f0f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1275\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1275"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Exercise: 定义一个计算数值累加的函数\n",
    "\n",
    "def mysum(num):\n",
    "    total=0\n",
    "    for i in range(1,num+1):\n",
    "        total += i\n",
    "    print(total)\n",
    "    return total\n",
    "\n",
    "mysum(50)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3004201a",
   "metadata": {},
   "source": [
    "## 局部变量 & 全局变量\n",
    "\n",
    "- 局部变量：定义在函数内的变量，拥有一个局部作用域; \n",
    "  - 作用范围：只能在其被声明的函数内部访问\n",
    "  - 为了临时保存数据需要在函数中定义变量进行存储\n",
    "  - 当函数调用时，局部变量被创建，当函数调用完，这个变量就不能再使用了\n",
    "\n",
    "- 全局变量：定义在函数外的变量，拥有全局作用域\n",
    "  - 作用范围：全局变量可以在整个程序范围内访问；可以在所有函数中使用\n",
    "\n",
    "- 局部变量与全局变量的冲突问题\n",
    "  - 当函数内出现局部变量和全局变量相同名字时，函数内部中的 `变量名=数据`, 此时将该变量理解为定义了一个函数内的局部变量，而不是修改全局变量的值\n",
    "  - 如果要在函数内修改全局变量，需要在函数内部声明 `global variable`  i.e. `global name`\n",
    "<!-- 调用函数时，所有在函数内声明的变量名称都将被加入到作用域中。 -->"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "0e4fbef1",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-03-10T04:19:37.606053Z",
     "start_time": "2022-03-10T04:19:37.588103Z"
    }
   },
   "outputs": [],
   "source": [
    "#函数外定义的称为全局变量\n",
    "name = 'june'\n",
    "def fun1():\n",
    "    print(name) # name可以在函数内部调用\n",
    "\n",
    "def fun2(str1, int1):\n",
    "    #global name  # 声明是全局变量\n",
    "    name = str1\n",
    "    print(name)    \n",
    "    age= int1 # 定义局部变量\n",
    "    print(age)\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "b6b97faf",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-03-10T04:19:38.648539Z",
     "start_time": "2022-03-10T04:19:38.626598Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "june\n",
      "fun1()  None\n",
      "hey\n",
      "18\n",
      "------------\n",
      "june\n",
      "---------\n"
     ]
    },
    {
     "ename": "NameError",
     "evalue": "name 'age' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_14172/712092618.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'---------'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 6\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mage\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m: name 'age' is not defined"
     ]
    }
   ],
   "source": [
    "print('fun1() ', fun1()) \n",
    "fun2('hey',18)\n",
    "print('------------')\n",
    "print(name)\n",
    "print('---------')\n",
    "print(age)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "4dfee83b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ice\n",
      "-----------\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'ice'"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Python中定义函数时，若想在函数内部对函数外的变量进行操作，\n",
    "# 就需要在函数内部声明其为global\n",
    "\n",
    "name='june'\n",
    "def fun():\n",
    "    global name\n",
    "    name='ice'\n",
    "    print(name)\n",
    "    return\n",
    "fun()\n",
    "print('-----------')\n",
    "name\n",
    "\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "001d85d5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "a=1 # 在函数内部修改a的值\n",
    "def fun():\n",
    "    global a\n",
    "    a=a+1\n",
    "    print(a)\n",
    "    return \n",
    "\n",
    "fun()\n",
    "print(a)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "560dae2b",
   "metadata": {},
   "source": [
    "## 函数的返回值\n",
    "\n",
    "- 如果想把函数中的结果返回给调用者，需要在函数中使用`return`\n",
    "  - `return` 后面写要返回的结果，如果有多个结果，需要用`,`分隔"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4b122822",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n"
     ]
    }
   ],
   "source": [
    "def total(a,b):\n",
    "    return a+b\n",
    "\n",
    "c = total(1,2)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "efe6df3a",
   "metadata": {},
   "source": [
    "```{note}\n",
    "- 一个函数中可以有多个`return`语句，但是只要有一个`return`语句被执行到，那么函数就会结束。\n",
    "- 如果函数是顺序结构，写多个`return`是没有意义的，函数只执行第一个`return`，后面的return不会被执行。\n",
    "- 如果程序内含分支结构，可以为不同的场景执行不同的return\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ef04a96",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---1---\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 顺序结构的`return`，只执行第一个\n",
    "def show():\n",
    "    print('---1---')\n",
    "    return 1\n",
    "    print('---2---')\n",
    "    return 2\n",
    "    print('---3---')\n",
    "    return 3\n",
    "\n",
    "show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f49f83cc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "400\n",
      "6\n"
     ]
    }
   ],
   "source": [
    "# 函数内有分支结构，可以设定不同的return\n",
    "def show(x):\n",
    "    if x>10:\n",
    "        return x*x\n",
    "    return 2*x\n",
    "\n",
    "print(show(20))\n",
    "print(show(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8875248",
   "metadata": {},
   "source": [
    "- 如果`return`后面有多个变量，那么默认会把它们打包为一个元组进行返回"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "305941e6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 10, 11)\n",
      "----------\n",
      "1 10 11\n"
     ]
    }
   ],
   "source": [
    "def fun(a,b):\n",
    "    return a,b,a+b  #存储为元组\n",
    "\n",
    "c = fun(1,10)\n",
    "print(c)\n",
    "print('-'*10)\n",
    "d,e,f = fun(1,10)\n",
    "print(d,e,f)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82597e0c",
   "metadata": {},
   "source": [
    "## 递归函数\n",
    "\n",
    "- 在函数内部，可以调用其他函数。如果一个函数在内部调用自身本身，这个函数就是**递归函数**。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce65bb04",
   "metadata": {},
   "source": [
    "- i.e. 计算阶乘: $n!=1*2*3*\\cdots*n = n*(n-1)!$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e3eea912",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "120\n",
      "120\n"
     ]
    }
   ],
   "source": [
    "# method 1: 使用循环函数\n",
    "def fact(n):\n",
    "    result = 1\n",
    "    for i in range(1,n+1):\n",
    "        result *= i\n",
    "    return result\n",
    "# test\n",
    "print(fact(5))\n",
    "\n",
    "# method 2: 使用递归函数\n",
    "def fact2(n):\n",
    "    if n == 1:  # 递归边界（初始条件或终止条件）\n",
    "        return 1\n",
    "    return n*fact2(n-1)\n",
    "\n",
    "print(fact2(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d0fe5fe",
   "metadata": {},
   "source": [
    "- **递归是非常重要的算法思想**\n",
    "  - 递归使用的是分支（或分工）的思想：手段是把问题的规模缩小，进行问题的简化\n",
    "  - 最重要的是定义函数时**首要想到终止条件是什么**（什么时候结束了）—— **在做工作之前，先要想中止条件**\n",
    "  - 调用函数的时候，规模会越来越小，终止条件把越来越小的规模兜住\n",
    "\n",
    "- 递归思路：从待求的未知项出发，**逐次调用本身（同名的函数）**直到递归边界（终止条件）的求解过程\n",
    "\n",
    "- 递归函数的优点是定义简单，逻辑清晰。理论上，所有的递归函数都可以写成循环的方式，但循环的逻辑不如递归清晰。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c7b85f86",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 练习：计算斐波那契额数列，1,1,2,3,5,8..., 的第n项的数值\n",
    "\n",
    "def fib(n):\n",
    "    if (n == 1) or (n == 2):\n",
    "        return 1\n",
    "    return fib(n-1) + fib(n-2)\n",
    "\n",
    "fib(40)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3dce89b",
   "metadata": {},
   "source": [
    "- 问题？ `fib(n)`的算法复杂度高，当n数值较大时，会有大量的重复计算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "dd84cab6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "102334155"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 修改\n",
    "def fib2(n):\n",
    "    if (n == 1) or (n == 2):\n",
    "        return 1\n",
    "    pos = 3\n",
    "    a,b = 1,1\n",
    "    while pos <= n:\n",
    "        c = a + b\n",
    "        # update a and b\n",
    "        a,b = b, c\n",
    "        pos += 1\n",
    "    return c\n",
    "\n",
    "fib2(40)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f95a5a4",
   "metadata": {},
   "source": [
    "## 捕获异常"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03e71ca5",
   "metadata": {},
   "source": [
    "- `try... except... finally...` 错误处理机制\n",
    "- 当我们认为某些代码可能会出错时，就可以用try来运行这段代码，如果执行出错，则try后的代码块不会继续执行，而是直接跳转至except语句块（错误处理代码），执行完except后，如果有finally语句块，则执行finally语句块。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "86d6e9b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "def div(n):\n",
    "    try:\n",
    "        print('in try block')\n",
    "        _ = 10/n\n",
    "        print('result: ', _)\n",
    "    except ZeroDivisionError as e:\n",
    "    # except Exception as e:\n",
    "        print('except: ', e)\n",
    "    finally:\n",
    "        print('finally')\n",
    "    print('END')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49167bee",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "in try block\n",
      "except:  division by zero\n",
      "finally\n",
      "END\n"
     ]
    }
   ],
   "source": [
    "# print(div(10))\n",
    "div(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "081af252",
   "metadata": {},
   "source": [
    "- 错误种类有很多，如果有可能出现不同类型的错误，可以由不同的except语句块处理，或者说，可以使用多个except来捕获不同类型的错误。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7bf03260",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "in try block\n",
      "valueError: invalid literal for int() with base 10: 's'\n",
      "finally...\n",
      "END\n"
     ]
    }
   ],
   "source": [
    "a = '0'\n",
    "try:\n",
    "    print('in try block')\n",
    "    r=10/int(a)\n",
    "    print('result:', r)\n",
    "except ValueError as e:\n",
    "    print('valueError:', e) \n",
    "except ZeroDivisionError as e:\n",
    "    print('ZeroDivisionError:', e)\n",
    "finally:\n",
    "    print('finally...')\n",
    "print('END')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b2dff65",
   "metadata": {},
   "source": [
    "- 此外，我们也可以在`except`代码块后面加一个`else`，当没有错误发生时，会自动执行`else`下的代码块："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff3ecbb5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "in try block\n",
      "result: 5.0\n",
      "no error!\n",
      "finally...\n",
      "END\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    print('in try block')\n",
    "    r = 10/int('2')\n",
    "    print('result:', r)\n",
    "except ValueError as e:\n",
    "    print('valueError:', e)\n",
    "except ZeroDivisionError as e:\n",
    "    print('ZeroDivisionError:', e)\n",
    "else:\n",
    "    print('no error!')\n",
    "finally:\n",
    "    print('finally...')\n",
    "print('END')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25ab6ce9",
   "metadata": {},
   "source": [
    "- 常见的错误类型：https://docs.python.org/3/library/exceptions.html#exception-hierarchy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0568bf2a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 也可以这样使用\n",
    "try:\n",
    "    a = 10/0\n",
    "except:\n",
    "    a = 1\n",
    "\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21438ff5",
   "metadata": {},
   "source": [
    "## 匿名函数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e9ef3a0",
   "metadata": {},
   "source": [
    "- lambda 创建匿名函数,可以减少函数名称冲突\n",
    "\n",
    "- lambda 主体是一个表达式，只能写一行，而不是代码块，仅仅能在lambda表达式中封装有限的逻辑进去\n",
    "\n",
    "- lambda 函数拥有自己的命名空间，且不能访问自己参数列表之外或全局命名空间里的参数\n",
    "\n",
    "<br />\n",
    "\n",
    "- 在Python中，lambda的语法是唯一的。其形式如下：\n",
    "\n",
    "    - `lambda argument_list: expression`\n",
    "  \n",
    "      - lambda是Python预留的关键字\n",
    "      - argument_list和expression由用户自定义。\n",
    "\n",
    "      - expression是一个关于参数的表达式。表达式中出现的参数需要在argument_list中有定义，并且**表达式只能是单行的**。\n",
    "\n",
    "- `lambda argument_list: expression` 表示的是一个函数。这个函数叫做lambda函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "8d525283",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 0)"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = lambda x,y: (x+y, x-y)\n",
    "a(1,1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "3d98e0c0",
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (Temp/ipykernel_10024/3231066019.py, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  File \u001b[1;32m\"C:\\Users\\illus\\AppData\\Local\\Temp/ipykernel_10024/3231066019.py\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    a = lambda x: 2*x for x in range(1,10)\u001b[0m\n\u001b[1;37m                      ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "# expression 中不能有循环\n",
    "a = lambda x: 2*x for x in range(1,10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37cfb21d",
   "metadata": {},
   "source": [
    "## 一些内置函数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a1c8c6c",
   "metadata": {},
   "source": [
    "#### 数学函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4a326c55",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 绝对值\n",
    "abs(-10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f286e3fb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "max:  6\n",
      "min:  1\n",
      "sum:  13\n"
     ]
    }
   ],
   "source": [
    "# 最大值 max，最小值 min\n",
    "a=[1,2,6,4]\n",
    "print('max: ', max(a))\n",
    "print('min: ', min(a))\n",
    "print('sum: ', sum(a))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6395337",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'3.123'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 保留小数, round\n",
    "round(3.12348,3)\n",
    "\n",
    "# 格式化输出\n",
    "'{:.3f}'.format(3.12348)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "31372deb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "# 取整，大于变量的最小整数：ceil； 小于变量的最大整数，floor\n",
    "import math\n",
    "\n",
    "print(math.ceil(3.6))\n",
    "print(math.floor(4.3))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82d24e48",
   "metadata": {},
   "source": [
    "#### 随机函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "74474bec",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.18508838833921104"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import random\n",
    "# random number, uniform~(0,1)\n",
    "random.random()  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00bb3c42",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.026312134222001637"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#normal distribution  random.gauss(mu,sigma)\n",
    "random.gauss(0,1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a4c031d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# set seed; 设置种子   random.seed()\n",
    "random.seed(100)\n",
    "a=random.random()\n",
    "b=random.random()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8dd4a16e",
   "metadata": {},
   "outputs": [],
   "source": [
    "random.seed(100)\n",
    "a1=random.random()\n",
    "b1=random.random()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "80c963e8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "a==a1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50e149ac",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 从序列中随机取一个数：random.choice()\n",
    "random.choice([1,2,3,4])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e6b112fc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#从一个范围内取随机数，random.randrange(start,end,step) 范围：start --- end-1\n",
    "random.randrange(1,11)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1200f18",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[7, 1, 3, 6, 4]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 打乱序列顺序， random.shuffle()  将原序列随机排序\n",
    "a=[1,7,4,6,3]\n",
    "random.shuffle(a)\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50b92187",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4, 7, 6]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 取样本， random.sample(seq., k)\n",
    "random.sample(a,3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cfa71e0",
   "metadata": {},
   "source": [
    "#### 常量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e371995c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.141592653589793\n",
      "2.718281828459045\n"
     ]
    }
   ],
   "source": [
    "# pi, e \n",
    "import math\n",
    "print(math.pi)\n",
    "print(math.e)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f3dad40",
   "metadata": {},
   "source": [
    "## 补充"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd4c9d83",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function fun at 0x0000021B349C0790>\n",
      "4\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fun2 = fun  # 用等号起别名\n",
    "print(fun2)\n",
    "fun2()\n",
    "fun2 is fun"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "644ce838",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 函数的自变量可以为函数\n",
    "def fun(a,b,f):\n",
    "    return f(a)+f(b)\n",
    "\n",
    "fun(1,-10,abs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1bce5ae6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# NOTE: 函数内部也可以内嵌函数\n",
    "def fun(a,b,c):\n",
    "    def sub_fun1(d,c):\n",
    "        pass\n",
    "    def sub_fun2(a,e):\n",
    "        pass"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
